// 本文件用于实现http协议
// http协议无需对报文添加边界信息，其本身的结构化数据里就包含了报文信息
// 所以在http协议实现时，我们只需要进行序列化和反序列化即可
#pragma once

#include <iostream>
#include <string>
#include <vector>
#include <sstream>

namespace MyHttp
{
    const std::string sep_blank = " ";                 // 词分隔符
    const std::string sep_line = "\r\n";        // 行分隔符
    const std::string first_path = "./wwwroot"; // 存放网页资源的根目录
    const std::string home_page = "index.html"; // 网页首页
    const std::string html_404 = first_path + "/404.html";
    // 请求方
    class HttpRequest
    {
    private:
        // 将资源所在路径，提取出资源后缀
        void Suffix()
        {
            int pos = _fpath.rfind(".");
            if (pos != std::string::npos)
            {
                // 提取成功
                _fsuffix = _fpath.substr(pos);
            }
            else
            {
                std::cout << "提取文件后缀失败" << std::endl;
            }
        }
        // 将url转换为对应的资源所在路径
        void URL_To_Path()
        {
            // 直接访问网址，_req_url为根目录，应该返回首页的信息
            if (_req_url == "/")
            {
                _fpath += "/" + home_page;
            }
            // req_url里自带资源路径：/a/b/c/d?wd=3&XXX&XXX
            else
            {
                // url中有资源路径
                _fpath += _req_url;
            }
        }

        // 拿到请求方的每一行字段
        bool GetLine(std::string &message, std::string *line)
        {
            int pos = message.find(sep_line);
            if (pos != std::string::npos) // 找到了
            {
                *line = message.substr(0, pos);
                message.erase(0, pos + sep_line.size());
                return true;
            }
            return false;
        }

        // 解析请求行内容
        void Parse_Req_Line()
        {
            // 使用流式的方法获取以空格为分隔符的子串
            std::stringstream ss(_req_line);
            ss >> _req_method >> _req_url >> _req_version;
        }

    public:
        HttpRequest()
            : _fpath(first_path)
        {
        }
        ~HttpRequest() {}

        // 读取资源内容
        bool ReadFileHelper(std::string &content, const std::string& fpath)
        {
            std::ifstream in(fpath, std::ios::binary);
            if (!in.is_open())
            {
                std::cout << "打开资源文件失败" << std::endl;
                return false;
            }
            in.seekg(0, in.end);   // 改变读取偏移量到文本末尾
            int size = in.tellg(); // 计算出总大小
            content.resize(size);
            in.seekg(0, in.beg); // 恢复读取偏移量到开始
            in.read((char *)content.c_str(), size);
            in.close();
            return true;
        }

        bool ReadFile(std::string &content)
        {
            return ReadFileHelper(content, _fpath);
        }

        bool Get404(std::string &content)
        {
            return ReadFileHelper(content, html_404);
        }


        // 序列化
        std::string Serialize()
        {
            return std::string();
        }

        // 反序列化
        void Deserialize(std::string &message)
        {
            // 获取请求行
            if (!GetLine(message, &_req_line)) // 获取失败 ，直接返回
                return;

            std::string tmp;
            while (true)
            {
                int ok = GetLine(message, &tmp);
                if (ok && tmp.empty()) // 找到空行
                {
                    _req_zero_line = "\r\n";
                    _req_message = message;
                    break;
                }
                else if (ok && !tmp.empty()) // 找到请求报头
                {
                    _req_headers.push_back(tmp);
                }
                else // 没获取到
                    break;
            }
        }

        // 解析http请求报文中的每个字段
        void Parse()
        {
            // 1. 解析请求行的内容，"method URL version"
            Parse_Req_Line();
            // 2. 将url的路径解析为资源路径
            URL_To_Path();
            // 3. 将资源路径的后缀提取出来
            Suffix();
        }

        // 测试打印
        void DebugPrint()
        {
            std::cout << "_req_line ---- " << _req_line << std::endl;
            int i = 1;
            for (auto &e : _req_headers)
            {
                printf("_req_headers-%d ---- %s\n", i++, e.c_str());
            }
            std::cout << "_req_zero_line ---- " << _req_zero_line << std::endl;
            std::cout << "_req_message ---- " << _req_message << std::endl;
        }
        // 测试打印出请求行的各种字段
        void Debug_Req_Line()
        {
            std::cout << "_req_method ---- " << _req_method << std::endl;
            std::cout << "_req_url ---- " << _req_url << std::endl;
            std::cout << "_req_version ---- " << _req_version << std::endl;
        }

        // 拿到url信息
        const std::string &GetURL() { return _req_url; }
        // 拿到请求资源路径
        const std::string &GetFilePath() { return _fpath; }
        // 拿到请求资源的后缀
        const std::string &GetFileSuffix() { return _fsuffix; }

    private:
        // 请求报文格式
        std::string _req_line;                 // 请求行
        std::vector<std::string> _req_headers; // 请求报头组【可以为空】
        std::string _req_zero_line;            // 空行 -》用于报头与有效载荷分离
        std::string _req_message;              // 有效载荷【可以为空】

        // 请求行的解析
        std::string _req_method;  // 请求行的请求方法
        std::string _req_url;     // 请求行的url
        std::string _req_version; // 请求行的版本信息，表明当前浏览器的版本

        // url的解析
        std::string _fpath;   // 请求资源的路径
        std::string _fsuffix; // 请求资源的后缀
    };

    class HttpResponse
    {
    public:
        HttpResponse()
            : _resp_version("Http/1.0"), _resp_status("200"), _resp_status_message("OK"), _resp_zero_line(sep_line)
        {
        }
        ~HttpResponse() {}

    public:
        // 更新状态码
        void SetRespNum(const std::string &resp_status)
        {
            _resp_status = resp_status;
        }
        // 更新状态信息
        void SetRespNumMessage() 
        { 
            Status_Description(std::stoi(_resp_status), _resp_status_message); 
        }
        // 添加有效载荷
        void AddMessage(const std::string& resp_message)
        {
            _resp_message = resp_message;
        }
        //增加响应报头的正文长度信息
        void AddContentLength(const std::string& length)
        {
            AddHeader("Content-Length", length);
        }
        // 增加响应报头的正文信息类型
        void AddContentType(const std::string& type)
        {
            AddHeader("Content-Type", type);
        }
        // 增加响应报头的重定向信息
        void AddLocation(const std::string& location)
        {
            AddHeader("Location", location);
        }
       
        // 构建序列化格式
        void Serialize(std::string& response)
        {
            // 1. 更新成最新的状态行
            CreateRespLine();
            // 拼接
            response = _resp_line + sep_line;
            for(auto& e : _resp_headers)
            {
                response += e + sep_line;
            }
            response += _resp_zero_line;
            response += _resp_message;
        }

        
    private:

        // 响应报文格式
        std::string _resp_line;                 // 状态行
        std::vector<std::string> _resp_headers; // 响应报头组
        std::string _resp_zero_line;            // 空行 -> 用于报头与有效载荷分离
        std::string _resp_message;              // 有效载荷

        // 状态行的解析
        std::string _resp_version;     // 服务器的http版本信息
        std::string _resp_status;         // 状态码
        std::string _resp_status_message; // 状态码描述

    private:
        // 构建状态行
        void CreateRespLine()
        {
            _resp_line = _resp_version + sep_blank + _resp_status + sep_blank + _resp_status_message;
        }
        // 添加响应报头
        void AddHeader(const std::string& header_name, const std::string& header_message)
        {
            _resp_headers.push_back(header_name + ":" + header_message);
        }

        // 状态码 -> 状态码描述
        void Status_Description(int status_code, std::string &description)
        {
            switch (status_code)
            {
            case 100:
                description = "Continue";
                break;
            case 101:
                description = "Switching Protocols";
                break;
            case 200:
                description = "OK";
                break;
            case 201:
                description = "Created";
                break;
            case 202:
                description = "Accepted";
                break;
            case 203:
                description = "Non-Authoritative Information";
                break;
            case 204:
                description = "No Content";
                break;
            case 205:
                description = "Reset Content";
                break;
            case 206:
                description = "Partial Content";
                break;
            case 300:
                description = "Multiple Choices";
                break;
            case 301:
                description = "Moved Permanently";
                break;
            case 302:
                description = "Found";
                break;
            case 303:
                description = "See Other";
                break;
            case 304:
                description = "Not Modified";
                break;
            case 305:
                description = "Use Proxy";
                break;
            case 307:
                description = "Temporary Redirect";
                break;
            case 400:
                description = "Bad Request";
                break;
            case 401:
                description = "Unauthorized";
                break;
            case 402:
                description = "Payment Required";
                break;
            case 403:
                description = "Forbidden";
                break;
            case 404:
                description = "Not Found";
                break;
            case 405:
                description = "Method Not Allowed";
                break;
            case 406:
                description = "Not Acceptable";
                break;
            case 407:
                description = "Proxy Authentication Required";
                break;
            case 408:
                description = "Request Timeout";
                break;
            case 409:
                description = "Conflict";
                break;
            case 410:
                description = "Gone";
                break;
            case 411:
                description = "Length Required";
                break;
            case 412:
                description = "Precondition Failed";
                break;
            case 413:
                description = "Request Entity Too Large";
                break;
            case 414:
                description = "Request-URI Too Long";
                break;
            case 415:
                description = "Unsupported Media Type";
                break;
            case 416:
                description = "Requested Range Not Satisfiable";
                break;
            case 417:
                description = "Expectation Failed";
                break;
            case 500:
                description = "Internal Server Error";
                break;
            case 501:
                description = "Not Implemented";
                break;
            case 502:
                description = "Bad Gateway";
                break;
            case 503:
                description = "Service Unavailable";
                break;
            case 504:
                description = "Gateway Timeout";
                break;
            case 505:
                description = "HTTP Version Not Supported";
                break;
            default:
                description = "Invalid Status Code";
            }
        }
    };

};